package com.twohand.admin.Service.impl;
import com.alibaba.fastjson2.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.twohand.admin.Dto.WxOrderListQueryDto;
import com.twohand.admin.Dto.WxPreOrderDto;
import com.twohand.admin.Dto.pay.RefundDto;
import com.twohand.admin.Entity.WxOrder;
import com.twohand.admin.Entity.WxRefund;
import com.twohand.admin.Mapper.BillMapper;
import com.twohand.admin.Mapper.WxOrderMapper;
import com.twohand.admin.Mapper.WxRefundMapper;
import com.twohand.admin.Service.WxOrderService;
import com.twohand.admin.Util.PageResponse;
import com.twohand.admin.Util.WxOrderUtil;
import com.twohand.admin.Vo.BillSearchResult;
import com.twohand.admin.Vo.ExpiredTime;
import com.twohand.admin.Vo.WxPayOrderNotifyCallbackResultVo;
import com.twohand.admin.Vo.pay.PayOrderListVo;
import com.twohand.utils.Response.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;

@Service
public class WxOrderServiceImpl implements WxOrderService {
    @Autowired
    WxPayService wxPayService;
    @Autowired
    BillMapper billMapper;
    @Autowired
    WxOrderMapper wxOrderMapper;
    @Autowired
    WxRefundMapper wxRefundMapper;

    @Value("${wx.pay.refundNotifyUrl}")
    String refundNotifyUrl;
    Logger log = LoggerFactory.getLogger(WxOrderServiceImpl.class);
    @Override
    public WxOrder preCreateOrder(WxPreOrderDto wxPreOrderDto) throws WxPayException {
        if(wxPreOrderDto==null || wxPreOrderDto.getBillNoList().size()==0){
            throw new BaseException(500,"请选择要支付的账单");
        }
        //1.获取订单总金额
        List<String> billNoList = wxPreOrderDto.getBillNoList();
        BigDecimal totalPrice = BigDecimal.ZERO;
        List<BillSearchResult> billSearchResults = billMapper.getBillDetailList(
                null,
                null,
                0,
                billNoList)
                ;
        if(billSearchResults.size()==0){
            throw new BaseException(500,"账单不存在或已支付");
        }
        //2.查看是否有相同订单已生成

        String openId = wxPreOrderDto.getOpenId();
        String billNoString = JSONArray.toJSONString(billNoList);

        List<WxOrder> wxOrderList = wxOrderMapper.orderIsExist(openId,billNoString);
        int size = wxOrderList.size();
        if(size > 0){
            log.info("有订单已经存在:{},size:{}:",wxOrderList,size);
            return wxOrderList.get(size-1);
        }

        //店铺名称
        String shopName = null;
        //店铺id
        String shopId = null;
        //计算订单总金额
        for (BillSearchResult bill:billSearchResults ) {
//            totalPrice += bill.getOrderMoney();
            totalPrice = totalPrice.add(new BigDecimal(Float.toString(bill.getOrderMoney())));
            //记录店铺名称
            shopName = bill.getShopName();
            //记录店铺id
            shopId = bill.getShopId();
        }
        ExpiredTime expiredTime = WxOrderUtil.getTime();
        //wxorder对象-->生成预支付单后存到数据库的
        WxOrder wxOrder = new WxOrder();
        wxOrder.setNo(WxOrderUtil.genWxOrderNo());
        wxOrder.setExpiredTime(expiredTime.getExpiredTime());
        wxOrder.setOpenId(openId);
        wxOrder.setTotalPrice(totalPrice.floatValue());
        wxOrder.setShopName(shopName);
        //构建请求参数
        //v3
        WxPayUnifiedOrderV3Request orderV3Request = new WxPayUnifiedOrderV3Request();
        //商品描述
        orderV3Request.setDescription("霸王餐收款");
        //商户订单号
        orderV3Request.setOutTradeNo(wxOrder.getNo());
        //交易结束时间
        orderV3Request.setTimeExpire(expiredTime.getExpiredTimeRfc());
        //附加数据
//        String attach = JSONArray.toJSONString(billNoList);
//        orderV3Request.setAttach(attach);
        //订单金额
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        //总金额 单位分
        amount.setTotal(BaseWxPayRequest.yuan2Fen(totalPrice));
        //货币类型
        amount.setCurrency("CNY");
        orderV3Request.setAmount(amount);
        //支付者
        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        payer.setOpenid(wxOrder.getOpenId());
        orderV3Request.setPayer(payer);
//        return orderRequest;
        WxPayUnifiedOrderV3Result.JsapiResult jsapiResult =  wxPayService.createOrderV3(TradeTypeEnum.JSAPI,orderV3Request);
//
        wxOrder.setAppId(jsapiResult.getAppId());
        wxOrder.setNonceStr(jsapiResult.getNonceStr());
        wxOrder.setPackageValue(jsapiResult.getPackageValue());
        wxOrder.setTimestamp(jsapiResult.getTimeStamp());
        wxOrder.setPaySign(jsapiResult.getPaySign());
        wxOrder.setBillNoList(billNoList);
        log.info("保存的对象wxOrder:{}",wxOrder);
        wxOrderMapper.insert(wxOrder);
        return wxOrder;
    }

    @Override
    public WxPayOrderNotifyCallbackResultVo  payNotify(String notifyData,String nonce,String timestamp, String serial,String signature){
        SignatureHeader signatureHeader = SignatureHeader.builder()
                .nonce(nonce)
                .timeStamp(timestamp)
                .serial(serial)
                .signature(signature)
                .build();
        WxPayOrderNotifyCallbackResultVo vo = new WxPayOrderNotifyCallbackResultVo("SUCCESS","OK");
        log.info("通知参数notifyData：{},signatureHeader:{}", notifyData, signatureHeader);
        WxPayOrderNotifyV3Result payResult = null;
        try{
            payResult = wxPayService.parseOrderNotifyV3Result(notifyData,signatureHeader);
        }catch (WxPayException e) {
            vo.setCode("FAIL");
            vo.setMessage("解析通知数据出错");
            log.info("解析通知数据出错：{}",e.getMessage());
        }
        WxPayOrderNotifyV3Result.DecryptNotifyResult result = payResult.getResult();
        log.info("解密后的数据:{}",result);
        /*
        * 获取单号
        * 1.将wx_order中的支付状态修改为已支付，录入支付时间
        * 2.设置bill中的wx_order外键关联
        * */
        //商户系统订单号
        String wxOrderNo = result.getOutTradeNo();
        //支付时间
        String successTime = result.getSuccessTime();
        //
        LambdaQueryWrapper<WxOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(WxOrder::getNo,wxOrderNo);
        WxOrder wxOrder = wxOrderMapper.selectOne(lambdaQueryWrapper);
        if(wxOrder == null){
            log.info("支付结果订单单号错误:{}",wxOrderNo);
            vo.setCode("FAIL");
            vo.setMessage("支付结果订单单号错误");
            return vo;
        }
        if(wxOrder.getSuccessTime() == null && wxOrder.getStatus() == 0){
            //未处理过逻辑
            //设置订单状态为支付成功,成功时间
            wxOrder.setSuccessTime(successTime);
            wxOrder.setStatus(1);
            List<String> billNoList =wxOrder.getBillNoList();
            //更新bill wx_order_id关联
            Integer count =  wxOrderMapper.updateWxOrderId(wxOrderNo,billNoList);
            log.info("{}的wx_order_id更新，更新数:{}",billNoList,count);
            wxOrderMapper.updateById(wxOrder);
        }
//        wxOrder.setPay
        return vo;
    }

    @Override
    public Object test(String orderNo) {
        LambdaQueryWrapper<WxOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(WxOrder::getNo,orderNo);
        WxOrder wxOrder = wxOrderMapper.selectOne(lambdaQueryWrapper);
        log.info("单号查one:{}",wxOrder);
        List<String> billNoList = wxOrder.getBillNoList();
        log.info("billnoList:{}",billNoList);
        return wxOrder;
    }


    public PageResponse<PayOrderListVo> getAllWxOrderList(WxOrderListQueryDto wxOrderListQueryDto){
        PageHelper.startPage(wxOrderListQueryDto.getPageNum(),wxOrderListQueryDto.getPageSize());
        String no = wxOrderListQueryDto.getNo();
        String openid = wxOrderListQueryDto.getOpenId();
        String billNo = wxOrderListQueryDto.getBillNoList();
        Integer status = wxOrderListQueryDto.getStatus();
        List<PayOrderListVo> list = wxOrderMapper.getList(no,openid,billNo,status);
        PageInfo<PayOrderListVo> pageInfo = new PageInfo<>(list);
        return new PageResponse<PayOrderListVo>(pageInfo,list);
    }
    public WxOrder getWxOrderById(Integer id){
        return wxOrderMapper.selectById(id);
    }
    public Boolean insertWxOrder(WxOrder wxOrder){
        return wxOrderMapper.insert(wxOrder) > 0;
    }
    public Boolean updateWxOrder(WxOrder wxOrder){
        return wxOrderMapper.updateById(wxOrder) > 0;
    }
    public Boolean deleteWxOrderById(Integer id){
        return wxOrderMapper.deleteById(id) > 0;
    }

    /**申请退款
     * @param refundDto 退款信息
     * @return
     */
    @Override
    public Boolean refund(RefundDto refundDto) throws WxPayException {
        //1.根据商户订单号查找相关订单
        String no = refundDto.getNo();
        LambdaQueryWrapper<WxOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(WxOrder::getNo,no);
        WxOrder wxOrder = wxOrderMapper.selectOne(lambdaQueryWrapper);
        if(wxOrder==null){
            throw new BaseException(500,"订单不存在");
        }
        if(wxOrder.getStatus()<1){
            throw new BaseException(500,"订单或未支付");
        }
        //查看退款金额
        BigDecimal refundPrice = null;
        //获取可退款金额
        BigDecimal totalPrice = new BigDecimal(Float.toString(wxOrder.getTotalPrice()));
        BigDecimal finalRefundPrice = getCanRefundCount(wxOrder.getNo());
        if(refundDto.getRefundPrice()!=null){
            refundPrice = new BigDecimal(Float.toString(refundDto.getRefundPrice()));
            if(finalRefundPrice.compareTo(refundPrice)<0){
                throw new BaseException(500,"退款金额必须小于等于:"+finalRefundPrice);
            }
            log.info("总金额：{}，退款金额:{}",totalPrice,refundPrice);
            finalRefundPrice = refundPrice;
        }
        //记录wx_refund
        WxRefund wxRefund = new WxRefund();
        wxRefund.setRefundNo(WxOrderUtil.genWxRefundNo());
        wxRefund.setStatus(0);
        wxRefund.setPrice(finalRefundPrice);
        wxRefund.setPayNo(wxOrder.getNo());
        wxRefund.setReason(refundDto.getReason());
        wxRefund.setShopName(wxOrder.getShopName());
        wxRefund.setShopId(wxOrder.getShopId());
        wxRefund.setBillNo(refundDto.getBillNo());


        WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
        //设置商户支付订单号
        wxPayRefundV3Request.setOutTradeNo(wxRefund.getPayNo());
        //设置商户退款订单号
        wxPayRefundV3Request.setOutRefundNo(wxRefund.getRefundNo());
        //设置退款金额信息
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        amount.setTotal(BaseWxPayRequest.yuan2Fen(totalPrice));
        amount.setRefund(BaseWxPayRequest.yuan2Fen(wxRefund.getPrice()));
        amount.setCurrency("CNY");
        wxPayRefundV3Request.setAmount(amount);
        //设置退款原因
        wxPayRefundV3Request.setReason(wxRefund.getReason());
        //设置通知地址
        wxPayRefundV3Request.setNotifyUrl(refundNotifyUrl);
        log.info("构造参数：{}",wxPayRefundV3Request);
        WxPayRefundV3Result wxPayRefundV3Result = wxPayService.refundV3(wxPayRefundV3Request);
        wxRefund.setWxRefundNo(wxPayRefundV3Result.getTransactionId());
        wxOrder.setStatus(3);
        wxOrderMapper.updateById(wxOrder);
        return wxRefundMapper.insert(wxRefund) > 0;
    }

    @Override
    public WxPayOrderNotifyCallbackResultVo refundNotity(String notifyData,String nonce,String timestamp, String serial,String signature) {
        SignatureHeader signatureHeader = SignatureHeader.builder()
                .nonce(nonce)
                .timeStamp(timestamp)
                .serial(serial)
                .signature(signature)
                .build();
        WxPayOrderNotifyCallbackResultVo vo = new WxPayOrderNotifyCallbackResultVo("SUCCESS","OK");
        WxPayRefundNotifyV3Result refundResult = null;
        try{
            refundResult = wxPayService.parseRefundNotifyV3Result(notifyData,signatureHeader);
        }catch (WxPayException e) {
            vo.setCode("FAIL");
            vo.setMessage("解析通知数据出错");
            log.info("解析通知数据出错：{}",e.getMessage());
        }
        WxPayRefundNotifyV3Result.DecryptNotifyResult result = refundResult.getResult();
        log.info("解密后的数据:{}",result);
        //商户退款号
        String outRefundNo = result.getOutRefundNo();
        //退款成功时间
        String successTime = result.getSuccessTime();
        //退款状态
        String refundStatus = result.getRefundStatus();

        LambdaQueryWrapper<WxRefund> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(WxRefund::getRefundNo,outRefundNo);
        log.info("商户自定义退款号：{}，退款成功时间：{}，refundStatus：{}",outRefundNo,successTime,refundStatus);
        //查找相关退款信息
        WxRefund wxRefund = wxRefundMapper.selectOne(lambdaQueryWrapper);
        log.info("找到的退款对象:{}",wxRefund);
        //查找支付订单信息
        LambdaQueryWrapper<WxOrder> wxOrderLambdaQueryWrapper = new LambdaQueryWrapper<>();
        wxOrderLambdaQueryWrapper.eq(WxOrder::getNo,wxRefund.getPayNo());
        WxOrder wxOrder = wxOrderMapper.selectOne(wxOrderLambdaQueryWrapper);
        log.info("找到的订单对象:{}",wxOrder);
        //设置退款状态成功
        if(refundStatus.equals("SUCCESS")){
            wxRefund.setStatus(1);
            wxOrder.setStatus(4);
            log.info("退款成功");
        }else if(refundStatus.equals("CHANGE")){
            wxRefund.setStatus(2);
            wxOrder.setStatus(5);
            log.info("退款异常");
        }else if(refundStatus.equals("REFUNDCLOSE")){
            wxRefund.setStatus(3);
            wxOrder.setStatus(6);
            log.info("退款关闭");
        }else{
            log.info("refundStatus 判断错误");
        }
        //设置退款时间
        wxRefund.setSuccessTime(successTime);
        wxRefundMapper.updateById(wxRefund);
        //设置订单退款状态
        wxOrderMapper.updateById(wxOrder);
        log.info("退款返回给微信的参数：{}",vo);
        return vo;
    }

    @Override
    public BigDecimal getCanRefundCount(String no) {
        LambdaQueryWrapper<WxOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(WxOrder::getNo,no);
        WxOrder wxOrder = wxOrderMapper.selectOne(lambdaQueryWrapper);
        if(wxOrder==null){
            throw new BaseException(500,"订单号有误");
        }
        BigDecimal totalPrice = new BigDecimal(Float.toString(wxOrder.getTotalPrice()));
        LambdaQueryWrapper<WxRefund> wxRefundLambdaQueryWrapper = new LambdaQueryWrapper<>();
        wxRefundLambdaQueryWrapper.eq(WxRefund::getPayNo,wxOrder.getNo()).eq(WxRefund::getStatus,1);
        List<WxRefund> wxRefundList = wxRefundMapper.selectList(wxRefundLambdaQueryWrapper);
        if(wxRefundList.size()>0){
            BigDecimal isRefundCount = new BigDecimal("0");
            for (WxRefund refund: wxRefundList) {
                isRefundCount = isRefundCount.add(refund.getPrice());
            }
            totalPrice = totalPrice.subtract(isRefundCount);
        }
        return totalPrice;
    }
}
